using System;
using System.Collections.Generic;
using System.IO;

using BeeDevelopment.Brazil;
using Stetson.Emulator.Hardware;

namespace Stetson.Emulator
{
	public partial class TI83Plus : Z80A, IDisposable
	{
		private const int PageSize = 0x4000;
		
		public bool FlashUnlocked;

		public TI83Plus(byte[] RomFile)
		{

			this.linkPort = new IOPort(this);
			this.lcdDriver = new T6A04(this);
			this.keypad = new Keypad();

			this.RomPages = new byte[32][];
			for (int i = 0; i < this.RomPages.Length; ++i)
			{
				this.RomPages[i] = new byte[PageSize];
				Array.Copy(RomFile, i * PageSize, this.RomPages[i], 0, PageSize);
			}

			this.RamPages = new byte[2][];
			for (int i = 0; i < this.RamPages.Length; ++i)
				this.RamPages[i] = new byte[PageSize];

			this.WriteMemoryPageA(0);
			this.WriteMemoryPageB(0);
			this.WriteMemoryMapControl(0);
		}

		public void Dispose()
		{
			if (this.lcdDriver != null) this.lcdDriver.Dispose();
		}

		int CyclesToRun = 0;
		const int CpuCyclesPerTimerTick = 183;

		int Timer1Pending = 0;
		int Timer2Pending = 0;
		
		public List<ushort> Breakpoints = new List<ushort>();
		public event EventHandler<BreakpointEventArgs> BreakpointHit;
		Stack<byte> RecentInstructions = new Stack<byte>(5);
        public bool IsExecuting = true;
        public List<TI83Plus> LinkedCalculators = new List<TI83Plus>();

        /// <summary>
        /// Virtually connects an emulator to this one via the link port
        /// </summary>
        /// <param name="Emulator"></param>
        public void LinkEmulator(TI83Plus Emulator)
        {
            LinkedCalculators.Add(Emulator);
        }

        public void UnlinkEmulator(TI83Plus Emulator)
        {
            LinkedCalculators.Remove(Emulator);
        }

		public new void FetchExecute(int cycles)
		{
            if (!IsExecuting)
                return;
			if (cycles == -1)
			{
				base.FetchExecute(-1);
				return;
			}
			CyclesToRun += cycles;
			while (CyclesToRun >= 0)
			{
				CyclesToRun -= CpuCyclesPerTimerTick;
				
				foreach (ushort Breakpoint in Breakpoints)
				{
					if (Breakpoint == RegisterPC)
					{
						if (BreakpointHit != null)
							BreakpointHit(this, new BreakpointEventArgs(Breakpoint));
					}
				}
				    
				this.RunTimerInterrupts();

				base.FetchExecute(CpuCyclesPerTimerTick);
			}
		}
	}
}